using System;
using System.Collections;
using Slaks.Graphs.Algorithms;

namespace Slaks.Graphs
{
	public class GraphList : CollectionBase
	{
		private Graph m_centroid = null;
		private double m_distanceToCentroid = -100;

		private ArrayList m_temporalGraphList = new ArrayList();

		public double GetAverageDistanceToCentroid()
		{
			return m_distanceToCentroid;
		}

		public Graph GetCentroid()
		{
			return m_centroid;
		}

		public void SetCentroid(Graph graph)
		{
			if (this.Contains(graph) == true) m_centroid = graph;
		}

		public void AddGraphToTemporalList(Graph graph)
		{
			m_temporalGraphList.Add(graph);
		}

		public void MergeTemporalList()
		{
			Graph graph;
			while (m_temporalGraphList.Count > 0)
			{
				graph = (Graph) m_temporalGraphList[0];
				this.Add(graph);
				m_temporalGraphList.RemoveAt(0);
			}
		}

		/// <summary>
		/// Determine centroid for the cluster of graphs 
		/// </summary>
		/// <param name="graphDistanceMeasure"></param>
		/// <returns>if the centroid changed return true otherwise return false</returns>
        public bool DetermineCentroid(GraphDistanceBase GDM)
        {
            GraphDistanceBase graphDistanceMeasure = GDM.clone();
            if (this.Count == 0) return false;

            Graph oldCentroid = m_centroid;
            Graph candidateCentroid = null;

            int size = this.Count;

            double minDistance = double.MaxValue;
            double distance = 0.0;
            double[,] distanceMap = new double[size, size];

            int i = 0;
            int j = 0;

            for (i = 0; i < size; i++)
                for (j = 0; j < size; j++)
                {
                    distanceMap[i, j] = distanceMap[j, i] = -100;
                }


            i = j = 0;

            double tempDistance;

            for (i = 0; i < size; i++)
            {
                Graph graph1 = this[i];
                distance = 0.0;

                for (j = 0; j < size; j++)
                {
                    Graph graph2 = this[j];
                    if (graph1 == graph2)
                    {
                        continue;
                    }
                    if (distanceMap[i, j] >= 0) distance += distanceMap[i, j];
                    else if (distanceMap[j, i] >= 0) distance += distanceMap[i, j];
                    else
                    {
                        graphDistanceMeasure.firstGraph = graph1;
                        graphDistanceMeasure.secondGraph = graph2;
                        tempDistance = graphDistanceMeasure.GetDistance ();
                        distance += tempDistance;
                        distanceMap[j, i] = distanceMap[i, j] = tempDistance;
                    }
                }

                distance /= size;

                if (minDistance > distance + 0.0000001)
                {
                    minDistance = distance;
                    candidateCentroid = graph1;
                }
            }

            m_distanceToCentroid = minDistance;
            m_centroid = candidateCentroid;

            if (oldCentroid == m_centroid) return false;
            return true;
        }

		#region CollectionBase

		public Graph this[int index]
		{
			get { return ((Graph) List[index]); }
			set { List[index] = value; }
		}

		public int Add(Graph value)
		{
			return (List.Add(value));
		}

		public int IndexOf(Graph value)
		{
			return (List.IndexOf(value));
		}

		public void Insert(int index, Graph value)
		{
			List.Insert(index, value);
		}

		public void Remove(Graph value)
		{
			List.Remove(value);
		}

		public bool Contains(Graph value)
		{
			// If value is not of type Graph, this will return false.
			return (List.Contains(value));
		}

		protected override void OnInsert(int index, Object value)
		{
			// Insert additional code to be run only when inserting values.
		}

		protected override void OnRemove(int index, Object value)
		{
			// Insert additional code to be run only when removing values.
		}

		protected override void OnSet(int index, Object oldValue, Object newValue)
		{
			// Insert additional code to be run only when setting values.
		}

		protected override void OnValidate(Object value)
		{
			// This check is commented out since it doesn't work for all types
			//if ( value.GetType() != Type.GetType("Graph") )
			//	throw new ArgumentException( "value must be of type Graph.", "value" );
		}

		#endregion
	}
}